home *** CD-ROM | disk | FTP | other *** search
- /* FTP client (interactive user) code */
- #include <stdio.h>
- #include "global.h"
- #include "mbuf.h"
- #include "session.h"
- #include "cmdparse.h"
- #include "proc.h"
- #include "tty.h"
- #include "socket.h"
- #include "ftp.h"
- #include "ftpcli.h"
-
- static int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),
- domkdir(),dormdir(),doascii(),dobinary(),doquit();
- static int getsub();
- static void sendport();
- struct mbuf *getline();
- int getresp();
-
- extern struct session *Current;
- extern char Nospace[],Badhost[];
- extern char *Tcpstates[],*Reasons[],*Unreach[],*Exceed[];
- extern int32 Clock;
- static char Notsess[] = "Not an FTP session!\n";
- static char Cantwrite[] = "Can't write %s\n";
- static char Cantread[] = "Can't read %s\n";
-
- struct cmds Ftpcmds[] = {
- "", donothing, 0, 0, NULLCHAR,
- "ascii", doascii, 0, 0, NULLCHAR,
- "binary", dobinary, 0, 0, NULLCHAR,
- "cd", doftpcd, 0, 2, "cd <directory>",
- "dir", dolist, 0, 0, NULLCHAR,
- "list", dolist, 0, 0, NULLCHAR,
- "get", doget, 0, 2, "get remotefile <localfile>",
- "ls", dols, 0, 0, NULLCHAR,
- "mkdir", domkdir, 0, 2, "mkdir <directory>",
- "nlst", dols, 0, 0, NULLCHAR,
- "quit", doquit, 0, 0, NULLCHAR,
- "rmdir", dormdir, 0, 2, "rmdir <directory>",
- "put", doput, 0, 2, "put localfile <remotefile>",
- "type", dotype, 0, 0, NULLCHAR,
- NULLCHAR, NULLFP, 0, 0, NULLCHAR,
- };
-
- /* Handle top-level FTP command */
- doftp(argc,argv)
- int argc;
- char *argv[];
- {
- struct session *sp;
- struct ftpcli ftp;
- struct sockaddr_in fsocket;
- int resp;
- struct mbuf *bp,*bpsav;
- char *cp;
- int control;
-
- /* Allocate a session control block */
- if((sp = newsession(argv[1],FTP)) == NULLSESSION){
- printf("Too many sessions\n");
- return 1;
- }
- memset((char *)&ftp,0,sizeof(ftp));
- ftp.control = ftp.data = -1;
-
- sp->cb.ftp = &ftp; /* Downward link */
- ftp.session = sp; /* Upward link */
- ftp.output = Curproc;
-
- fsocket.sin_family = AF_INET;
- if(argc < 3)
- fsocket.sin_port = IPPORT_FTP;
- else
- fsocket.sin_port = atoi(argv[2]);
-
- if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
- printf(Badhost,sp->name);
- freesession(sp);
- return 1;
- }
- /* Open the control connection */
- control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0);
- printf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
- if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
- goto quit;
- printf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
- sp->name);
-
- /* Wait for greeting from server */
- resp = getresp(control,200);
-
- /* Now process responses and commands */
- for(;;){
- switch(resp){
- case -1:
- goto quit;
- case 220: /* Sign-on banner; prompt for and send USER command */
- printf("Enter user name: ");
- bp = qdata("USER ",5);
- append(&bp,getline(sp));
- if(send_mbuf(control,bp,0,NULLCHAR,0) == -1)
- goto quit;
- resp = getresp(control,200);
- break;
- case 331: /* Password prompt; get password */
- printf("Password: ");
- ttysetmode(TTY_EDIT); /* turn off echo */
- bp = qdata("PASS ",5);
- append(&bp,getline(sp));
- printf("\r\n");
- if(send_mbuf(control,bp,0,NULLCHAR,0) == -1)
- goto quit;
- ttysetmode(TTY_EDIT|TTY_ECHO);
- resp = getresp(control,200);
- break;
- default:
- /* Test the control channel first */
- if(sockstate(control) == NULLCHAR)
- goto quit;
-
- printf("ftp> ");
- bp = getline(sp);
-
- /* Copy because cmdparse modifies the original */
- bpsav = copy_p(bp,len_mbuf(bp));
- if((resp = cmdparse(Ftpcmds,bp->data)) != -1){
- /* Valid command, free buffer and get another */
- free_p(bpsav);
- } else {
- /* Not a local cmd, send to remote server */
- if(send_mbuf(control,bpsav,0,NULLCHAR,0) == -1){
- free_p(bp);
- goto quit;
- }
- resp = getresp(control,200);
- }
- free_p(bp);
- break;
- }
- }
- quit: cp = sockerr(control);
- printf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
- cp != NULLCHAR ? cp : "EOF");
-
- if(ftp.fp != NULLFILE && ftp.fp != stdout)
- fclose(ftp.fp);
- if(ftp.data != -1)
- close_s(ftp.data);
- if(ftp.control != -1)
- close_s(ftp.control);
- if(ftp.session != NULLSESSION)
- freesession(ftp.session);
- return 0;
- }
-
- /* Handle null line to avoid trapping on first command in table */
- static
- int
- donothing(argc,argv)
- int argc;
- char *argv[];
- {
- }
- /* Close session */
- static int
- doquit(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftpcli *ftp;
- int control;
-
- ftp = Current->cb.ftp;
- control = ftp->control;
- usprintf(control,"QUIT\r\n");
- getresp(control,200); /* Get the closing message */
- getresp(control,200); /* Wait for the server to close */
- }
-
- /* Translate 'cd' to 'cwd' for convenience */
- static
- int
- doftpcd(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftpcli *ftp;
-
- ftp = Current->cb.ftp;
- usprintf(ftp->control,"CWD %s\r\n",argv[1]);
- return getresp(ftp->control,200);
- }
- /* Translate 'mkdir' to 'xmkd' for convenience */
- static
- int
- domkdir(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftpcli *ftp;
-
- ftp = Current->cb.ftp;
- usprintf(ftp->control,"XMKD %s\r\n",argv[1]);
- return getresp(ftp->control,200);
- }
- /* Translate 'rmdir' to 'xrmd' for convenience */
- static
- int
- dormdir(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftpcli *ftp;
-
- ftp = Current->cb.ftp;
- usprintf(ftp->control,"XRMD %s\r\n",argv[1]);
- return getresp(ftp->control,200);
- }
- static int
- dobinary()
- {
- char *argv[2];
-
- argv[1] = "i";
- dotype(2,argv);
- }
- static int
- doascii()
- {
- char *argv[2];
-
- argv[1] = "a";
- dotype(2,argv);
- }
-
- /* Handle "type" command from user */
- static
- int
- dotype(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftpcli *ftp;
- int control;
-
- ftp = Current->cb.ftp;
- control = ftp->control;
- if(argc < 2){
- switch(ftp->type){
- case IMAGE_TYPE:
- printf("Image\n");
- break;
- case ASCII_TYPE:
- printf("Ascii\n");
- break;
- case LOGICAL_TYPE:
- printf("Logical bytesize %u\n",ftp->logbsize);
- break;
- }
- return 0;
- }
- switch(*argv[1]){
- case 'i':
- case 'b':
- ftp->typesent = ftp->type = IMAGE_TYPE;
- usprintf(control,"TYPE I\r\n");
- break;
- case 'a':
- ftp->typesent = ftp->type = ASCII_TYPE;
- usprintf(control,"TYPE A\r\n");
- break;
- case 'l':
- ftp->typesent = ftp->type = LOGICAL_TYPE;
- ftp->logbsize = atoi(argv[2]);
- usprintf(control,"TYPE L %s\r\n",argv[2]);
- break;
- default:
- printf("Invalid type %s\n",argv[1]);
- return 1;
- }
- return getresp(control,200);
- }
- /* Start receive transfer. Syntax: get <remote name> [<local name>] */
- static
- doget(argc,argv)
- int argc;
- char *argv[];
- {
- char *remotename,*localname;
- register struct ftpcli *ftp;
-
- ftp = Current->cb.ftp;
- if(ftp == NULLFTP){
- printf(Notsess);
- return 1;
- }
- remotename = argv[1];
- if(argc < 3)
- localname = remotename;
- else
- localname = argv[2];
-
- return getsub(ftp,"RETR",remotename,localname);
- }
- /* List remote directory. Syntax: dir <remote files> [<local name>] */
- static
- dolist(argc,argv)
- int argc;
- char *argv[];
- {
- char *remotename,*localname;
- register struct ftpcli *ftp;
-
- ftp = Current->cb.ftp;
- if(ftp == NULLFTP){
- printf(Notsess);
- return 1;
- }
- remotename = argv[1];
- if(argc > 2)
- localname = argv[2];
- else
- localname = NULLCHAR;
- return getsub(ftp,"LIST",remotename,localname);
- }
- /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
- static
- dols(argc,argv)
- int argc;
- char *argv[];
- {
- char *remotename,*localname;
- register struct ftpcli *ftp;
-
- ftp = Current->cb.ftp;
- if(ftp == NULLFTP){
- printf(Notsess);
- return 1;
- }
- remotename = argv[1];
- if(argc > 2)
- localname = argv[2];
- else
- localname = NULLCHAR;
- return getsub(ftp,"NLST",remotename,localname);
- }
- /* Common code to LIST/NLST/RETR */
- static int
- getsub(ftp,command,remotename,localname)
- struct ftpcli *ftp;
- char *command,*remotename,*localname;
- {
- unsigned long total;
- FILE *fp;
- int cnt,resp,i,control;
- char *mode;
- struct sockaddr_in lsocket;
- int32 startclk,rate;
-
- control = ftp->control;
-
- switch(ftp->type){
- case IMAGE_TYPE:
- case LOGICAL_TYPE:
- mode = Binmode[WRITE_BINARY];
- break;
- case ASCII_TYPE:
- mode = "w";
- break;
- }
- /* Send TYPE message, if necessary */
- if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
- if(ftp->typesent != ASCII_TYPE){
- /* Directory listings are always in ASCII */
- usprintf(control,"TYPE A\r\n");
- ftp->typesent = ASCII_TYPE;
- resp = getresp(control,200);
- if(resp == -1 || resp > 299){
- return 1;
- }
- }
- } else if(ftp->typesent != ftp->type){
- switch(ftp->type){
- case ASCII_TYPE:
- usprintf(control,"TYPE A\r\n");
- break;
- case IMAGE_TYPE:
- usprintf(control,"TYPE I\r\n");
- break;
- case LOGICAL_TYPE:
- usprintf(control,"TYPE L %d\r\n",ftp->logbsize);
- break;
- }
- ftp->typesent = ftp->type;
- resp = getresp(control,200);
- if(resp == -1 || resp > 299){
- return 1;
- }
- }
- if(localname == NULLCHAR){
- fp = stdout;
- } else if((fp = fopen(localname,mode)) == NULLFILE){
- printf(Cantwrite,localname);
- return 1;
- }
- /* Open the data connection */
- ftp->data = socket(AF_INET,SOCK_STREAM,0);
- listen(ftp->data,0); /* Accept only one connection */
- ftp->state = RECEIVING_STATE;
-
- /* Send the PORT message and wait for ack */
- i = SOCKSIZE;
- getsockname(ftp->data,(char *)&lsocket,&i);
- sendport(control,&lsocket);
- resp = getresp(control,200);
- if(resp == -1 || resp > 299){
- close_s(ftp->data);
- ftp->data = -1;
- ftp->state = COMMAND_STATE;
- return 1;
- }
- /* Generate the command to start the transfer and wait for ack */
- if(remotename != NULLCHAR)
- usprintf(control,"%s %s\r\n",command,remotename);
- else
- usprintf(control,"%s\r\n",command);
- /* Get the intermediate "150" response */
- resp = getresp(control,100);
- if(resp == -1 || resp >= 400){
- /* Error, quit */
- close_s(ftp->data);
- ftp->data = -1;
- ftp->state = COMMAND_STATE;
- return 1;
- }
- /* Wait for the server to open the data connection */
- cnt = 0;
- ftp->data = accept(ftp->data,NULLCHAR,&cnt);
- startclk = Clock;
-
- total = recvfile(fp,ftp->data,ftp->type);
-
- #ifdef CPM
- if(ftp->type == ASCII_TYPE)
- fputc(CTLZ,fp);
- #endif
- startclk = Clock - startclk;
- if(startclk != 0)
- rate = total/startclk;
- else
- rate = 0;
- if(total != -1)
- printf("Get complete: %lu bytes in %lu sec (%lu/sec)\n",
- total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
- else
- printf("Error during data transfer\n");
- if(fp != stdout)
- fclose(fp);
- close_s(ftp->data);
- ftp->data = -1;
- getresp(control,200);
- return 0;
- }
- /* Send a file. Syntax: put <local name> [<remote name>] */
- static
- doput(argc,argv)
- int argc;
- char *argv[];
- {
- char *remotename,*localname,*mode;
- register struct ftpcli *ftp;
- int i,resp,control;
- unsigned long total;
- FILE *fp;
- struct sockaddr_in lsocket;
- int32 startclk,rate;
-
- ftp = Current->cb.ftp;
- control = ftp->control;
-
- if(ftp == NULLFTP){
- printf(Notsess);
- return 1;
- }
- localname = argv[1];
- if(argc < 3)
- remotename = localname;
- else
- remotename = argv[2];
-
- if(ftp->type == IMAGE_TYPE)
- mode = Binmode[READ_BINARY];
- else
- mode = "r";
-
- /* Send TYPE message, if necessary */
- if(ftp->typesent != ftp->type){
- switch(ftp->type){
- case ASCII_TYPE:
- usprintf(control,"TYPE A\r\n");
- break;
- case IMAGE_TYPE:
- usprintf(control,"TYPE I\r\n");
- break;
- case LOGICAL_TYPE:
- usprintf(control,"TYPE L %d\r\n",ftp->logbsize);
- break;
- }
- ftp->typesent = ftp->type;
- resp = getresp(control,200);
- if(resp == -1 || resp > 299){
- return 1;
- }
- }
- if((fp = fopen(localname,mode)) == NULLFILE){
- printf(Cantread,localname);
- return 1;
- }
- /* Open the data connection */
- ftp->data = socket(AF_INET,SOCK_STREAM,0);
- listen(ftp->data,0);
-
- /* Send the PORT message and wait for ack */
- i = SOCKSIZE;
- getsockname(ftp->data,(char *)&lsocket,&i);
- sendport(control,&lsocket);
- resp = getresp(control,200);
- if(resp == -1 || resp > 299){
- ftp->state = COMMAND_STATE;
- ftp->data = -1;
- return 1;
- }
- /* Generate the command to start the transfer and wait for ack */
- usprintf(control,"STOR %s\r\n",remotename);
- resp = getresp(control,100);
- if(resp == -1 || resp >= 400){
- /* Error, quit */
- ftp->state = COMMAND_STATE;
- ftp->data = -1;
- return 1;
- }
- /* Wait for the data connection to open. Otherwise the first
- * block of data would go out with the SYN, and this may confuse
- * some other TCPs
- */
- accept(ftp->data,NULLCHAR,(int *)NULL);
-
- startclk = Clock;
-
- total = sendfile(fp,ftp->data,ftp->type);
-
- startclk = Clock - startclk;
- if(startclk != 0)
- rate = total/startclk;
- else
- rate = 0;
- if(total != -1)
- printf("Put complete: %lu bytes in %lu sec (%lu/sec)\n",
- total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
- else
- printf("Error during data transfer\n");
-
- fclose(fp);
- close_s(ftp->data);
- ftp->data = -1;
- getresp(control,200);
- return 1;
- }
- /* Abort a GET or PUT operation in progress. Note: this will leave
- * the partial file on the local or remote system
- */
- doabort(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftpcli *ftp;
-
- if(Current == NULLSESSION || Current->type != FTP){
- printf("Not an active FTP session\n");
- return;
- }
- ftp = Current->cb.ftp;
-
- switch(ftp->state){
- case COMMAND_STATE:
- printf("No active transfer\n");
- return;
- case SENDING_STATE:
- /* Send a premature EOF.
- * Unfortunately we can't just reset the connection
- * since the remote side might end up waiting forever
- * for us to send something.
- */
- shutdown(ftp->data,1);
- break;
- case RECEIVING_STATE:
- /* Just exterminate the data connection; this will
- * generate a RST on the next data packet which will
- * abort the sender
- */
- close_s(ftp->data);
- break;
- }
- ftp->state = COMMAND_STATE;
- ftp->data = -1;
- printf("Transfer aborted\n");
- }
- /* send PORT message */
- static void
- sendport(s,socket)
- int s;
- struct sockaddr_in *socket;
- {
- struct mbuf *bp;
-
- /* Compose and send PORT a,a,a,a,p,p message */
- if((bp = alloc_mbuf(35)) == NULLBUF){ /* 5 more than worst case */
- printf(Nospace);
- return;
- }
- /* I know, this looks gross, but it works! */
- sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
- hibyte(hiword(socket->sin_addr.s_addr)),
- lobyte(hiword(socket->sin_addr.s_addr)),
- hibyte(loword(socket->sin_addr.s_addr)),
- lobyte(loword(socket->sin_addr.s_addr)),
- hibyte(socket->sin_port),
- lobyte(socket->sin_port));
- bp->cnt = strlen(bp->data);
- send_mbuf(s,bp,0,NULLCHAR,0);
- }
-
- /* Wait for, read and display response from FTP server. Return the result code.
- */
- int
- getresp(s,mincode)
- int s;
- int mincode; /* Keep reading until at least this code comes back */
- {
- register char *line;
- int rval;
-
- line = malloc(256);
- for(;;){
- /* Get line */
- if(recvline(s,line,256) == -1){
- rval = -1;
- break;
- }
- rip(line); /* Remove cr/lf */
- printf("%s\n",line); /* Display to user */
-
- /* Messages with dashes are continued */
- if(line[3] != '-' && (rval = atoi(line)) >= mincode)
- break;
- }
- free(line);
- return rval;
- }
-
- struct mbuf *
- getline(sp)
- struct session *sp;
- {
- /* Don't read from the keyboard unless we're the current session */
- while(sp->input == NULLBUF)
- pwait(&sp->input);
- return dequeue(&sp->input);
- }
- /* Allocate an FTP client control block */
- struct ftpcli *
- ftp_create()
- {
- register struct ftpcli *ftp;
-
- if((ftp = (struct ftpcli *)calloc(1,sizeof (struct ftpcli))) == NULLFTP)
- return NULLFTP;
-
- ftp->state = COMMAND_STATE;
- ftp->type = ASCII_TYPE; /* Default transfer type */
- ftp->typesent = ASCII_TYPE; /* Server default is ascii */
- ftp->control = ftp->data = -1;
- return ftp;
- }
-
-
-